// 
// Copyright (c) 2006-2008 Ben Motmans
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// Author(s):
//    Ben Motmans <ben.motmans@gmail.com>
//

using System;
using System.IO;
using System.Xml;
using System.Collections.Generic;

namespace Anculus.Core
{
	public sealed class XmlConfiguration : XmlConfigurationSection, IConfiguration, IDisposable
	{
		private Stream _stream;
		private string _filename;
		private bool _createStreamFromFilename;
		
		private bool _disposed;
		
		public XmlConfiguration (string filename)
			: base (null, "root")
		{
			if (filename == null)
				throw new ArgumentNullException ("filename");
			
			this._filename = filename;
			this._createStreamFromFilename = true;
		}
		
		public XmlConfiguration (Stream stream)
			: base (null, "root")
		{
			if (stream == null)
				throw new ArgumentNullException ("stream");
			
			this._stream = stream;
			this._createStreamFromFilename = false;
		}
		
		~XmlConfiguration ()
		{
			Dispose (false);
		}

		public void Load ()
		{
			if (_createStreamFromFilename) {
				if (!File.Exists (_filename))
					return;
				
				FileStream stream = null;
				try {
					stream = new FileStream (_filename, FileMode.Open, FileAccess.Read);
					Load (stream);
				} catch (Exception e) {
					Log.Error (e, "XmlConfiguration.Load");
					if (stream != null) {
						stream.Close ();
						stream.Dispose ();
					}
				}
			} else {
				Load (_stream);
			}
		}
		
		public void Save ()
		{
			if (_createStreamFromFilename) {
				FileStream stream = null;
				try {
					stream = new FileStream (_filename, FileMode.Create, FileAccess.Write);
					Save (stream);
				} catch (Exception e) {
					//TODO: don't catch this generic exception, catch IO/XML exceptions instead
					
					Log.Error (e, "XmlConfiguration.Save");
					if (stream != null) {
						stream.Close ();
						stream.Dispose ();
					}
				}
			} else {
				Save (_stream);
			}
		}
		
		public void Dispose ()
		{
			Dispose (true);
			GC.SuppressFinalize (this);
		}
		
		private void Dispose (bool disposing)
		{
			if (!_disposed) {
				if (disposing) {
					if (_stream != null && !_createStreamFromFilename)
						_stream.Dispose (); //only dispose the stream if it was created internally
				}
			}
			_disposed = true;
		}
		
		private void Save (Stream stream)
		{
			XmlTextWriter writer = new XmlTextWriter (stream, System.Text.Encoding.UTF8);
			writer.Formatting = Formatting.Indented;
			writer.IndentChar = '\t';
			writer.Indentation = 1;

			writer.WriteStartElement ("Configuration");
			writer.WriteAttributeString ("version", "2.0");

			SaveSectionVersion2 (writer);

			writer.WriteEndElement ();
			writer.Close ();
		}

		private void Load (Stream stream)
		{
			XmlReader reader = null;
			try {
				reader = new XmlTextReader (stream);

				while (reader.Read ()) {
					if (reader.Depth > 1 || reader.NodeType != XmlNodeType.Element)
						continue;
	
					if (reader.LocalName == "Configuration") {
						string version = reader.GetAttribute ("version");
						
						switch (version) {
						case "1.0":
							LoadSectionVersion1 (reader.ReadSubtree ());
							break;
						case "2.0":
							LoadSectionVersion2 (reader.ReadSubtree ());
							break;
						default:
							Log.Warn ("Configuration file format is not supported. (version={0})", version == null ? "null" : version);
							break;
						}
					}
				}
			} catch (Exception e) {
				//TODO: don't catch this generic exception, catch IO/XML exceptions instead
				Log.Error (e, "XmlConfiguration.Load");
			} finally {
				if (reader != null)
					reader.Close ();
			}
		}
	}
}